﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Interop;
using System.Windows.Media;
using System.IO;
using System.Threading;
using System.Xml.Linq;
using System.Xml;
using RoMBot;
using RoMBot.Classes;
using RoMBot.Database;
using RoMBot.Waypoints;
using System.Windows.Threading;
using Skill = RomBotUI.Helpers.Skill;
using System.ComponentModel;
using Key = RoMBot.Key;
using Keyboard = RoMBot.Keyboard;

namespace RomBotUI
{
    /// <summary>
    /// FM top 67 2943 sábado 11:30
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //internal static string DataPath = Environment.GetFolderPath( Environment.SpecialFolder.MyDocuments ) + "\\RoMBot\\";
        private readonly List<string> profiles = new List<string>();
        private readonly List<string> waypoints = new List<string>();
        XmlDataProvider skillsProvider = new XmlDataProvider();
        readonly DispatcherTimer dispatcherTimer = new DispatcherTimer();
        private SynchronizationContext context;
        Player player;
        delegate void UpdateUiDelegate();

        readonly ObjectsTable surrounding = new ObjectsTable();
        private readonly Dictionary<int, int> inventoryStatistics = new Dictionary<int, int>();
        private BindingList<InventoryItemStatistics> inventoryItems;
        public static Bot Bot { get { return Bot.Instance; } }
        private bool uiInitialized;

        public MainWindow()
        {
            AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
            InitializeComponent();
            Bot.MainWindowHandle = new WindowInteropHelper( this ).Handle;
            Logger.LogArrived += LoggerOnLogArrived;
            Bot.ProgressBarUpdate += Bot_ProgressBarUpdate;
            Bot.BotInitializated += this.BotInitializated;
            Bot.BotStopped += BotStopped;
            Bot.BotPaused += BotPaused;
            Bot.BotStartedOrResumed += this.BotStartedOrResumed;
            Bot.BotGameInstanceAdded += this.BotGameInstanceAdded;

            this.Closed += OnClosed;

            this.context = SynchronizationContext.Current;

            LoadProfiles();
            LoadWaypoints();

            if ( Bot.GameInstances.Count > 1 )
            {
                foreach ( var instance in Bot.GameInstances )
                {
                    CbGameInstances.Items.Add( instance.Key );
                }
            }
            else if ( Bot.GameInstances.Count == 1 )
            {
                var playerName = Bot.GameInstances.First().Key;
                CbGameInstances.Items.Add( playerName );
                CbGameInstances.SelectedItem = playerName;
                //CbGameInstances.IsEnabled = false;
                CbProfile.SelectedValue = playerName;
                if ( CbProfile.SelectedItem != null )
                {
                    this.BtnLoadClick( btnLoad, null );
                }
            }

            Bot.Print( "Interface loaded.", System.Drawing.Color.MediumPurple );
            //Database.StartDatabase();
            if ( Bot.ProcId != 0 && RoMBot.Helpers.IsInGame )
            {
                Dispatcher.BeginInvoke( new Func<bool>( () => Bot.Initialize() ) );
            }
        }

        void BotStartedOrResumed( object sender, EventArgs e )
        {
            Dispatcher.Invoke( new Action( () =>
            {
                this.btnPause.IsEnabled = true;
                this.btnStop.IsEnabled = true;
                this.btnStart.IsEnabled = false;
                this.BtnAssistMode.IsEnabled = false;
            } ) );
        }

        private void BotGameInstanceAdded( object sender, GameInstanceAddedEventArgs eventArgs )
        {
            Dispatcher.Invoke( new Action( () =>
                                      {
                                          if ( !this.CbGameInstances.Items.Contains( eventArgs.InstanceName ) )
                                          {
                                              this.CbGameInstances.Items.Add( eventArgs.InstanceName );
                                              if ( this.CbGameInstances.Items.Count > 1 && !this.CbGameInstances.IsEnabled )
                                              {
                                                  this.CbGameInstances.IsEnabled = true;
                                              }
                                          }
                                      } ) );
        }

        private void BotPaused( object sender, EventArgs e )
        {
            Dispatcher.Invoke( new Action( () =>
            {
                this.btnPause.IsEnabled = false;
                this.btnStop.IsEnabled = false;
                this.btnStart.IsEnabled = true;
                this.BtnAssistMode.IsEnabled = false;
            } ) );
        }

        private void BotStopped( object sender, EventArgs e )
        {
            Dispatcher.Invoke( new Action( () =>
            {
                this.btnPause.IsEnabled = false;
                this.btnStop.IsEnabled = false;
                this.btnStart.IsEnabled = true;
                this.BtnAssistMode.IsEnabled = true;
            } ) );
        }

        void BotInitializated( object sender, EventArgs e )
        {
            InitializeUI();
        }

        private void InitializeUI()
        {
            if ( this.player == null )
            {
                this.player = new Player();
            }
            else
            {
                this.player.Update();
            }

            this.inventoryStatistics.Clear();
            inventoryItems = new BindingList<InventoryItemStatistics>( this.player.Inventory.Items.Where( item => item.ItemCount > 0 ).Select( i => new InventoryItemStatistics( i ) ).ToList() );
            foreach ( var item in this.inventoryItems.Where( item => !this.inventoryStatistics.ContainsKey( item.Id ) ) )
            {
                this.inventoryStatistics.Add( item.Id, this.player.Inventory.GetItemCount( item.Id, false ) );
            }

            this.InventoryGrid.ItemsSource = inventoryItems;

            this.Title = string.Format( RoMBot.Properties.Resources.WindowTitle, player.Name, Bot.TimeToLevel );
            this.HpBar.Maximum = this.player.MaxHP;
            this.HpBar.Value = this.player.HP;
            this.TbHp.Text = this.player.HP + " / " + this.player.MaxHP;

            this.MpBar.Maximum = this.player.MaxMP;
            this.MpBar.Value = this.player.MP;
            this.TbMp.Text = this.player.MP + " / " + this.player.MaxMP;

            if ( this.player.Class1 > 0 && !Equals( this.MpBar.Foreground, TipoClaseColorEnergia[ this.player.Class1 ] ) )
            {
                this.MpBar.Foreground = TipoClaseColorEnergia[ this.player.Class1 ];
            }

            if ( ( this.player.Class2 <= 0 || this.player.Class2 == this.player.Class1 || Definitions.ClassEnergyMap[ this.player.Class1 ] == Definitions.ClassEnergyMap[ this.player.Class2 ] ) )
            {
                this.Mp2Bar.Visibility = Visibility.Hidden;
                this.TbMp2.Visibility = Visibility.Hidden;
                tbMp2Tipo.Visibility = Visibility.Hidden;
            }
            else if ( this.player.Class2 > 0 )
            {
                this.Mp2Bar.Foreground = TipoClaseColorEnergia[ this.player.Class2 ];
            }

            ExpBar.Maximum = player.MaxExp;
            TbExp.Text = player.Exp + " / " + player.MaxExp;

            this.LblName.Content = this.player.Name + " - [" + this.player.Class1 + " / " + this.player.Class2 + "]";

            btnStart.IsEnabled = true;
            BtnAssistMode.IsEnabled = true;
            uiInitialized = true;

            dispatcherTimer.Interval = TimeSpan.FromMilliseconds( 500 );
            dispatcherTimer.Tick += DispatcherTimerTick;
            dispatcherTimer.Start();
        }

        void Bot_ProgressBarUpdate( object sender, ProgressbarUpdatedArgs e )
        {
            Dispatcher.BeginInvoke( new Action( () =>
                                                {
                                                    if ( e.Finalize )
                                                    {
                                                        LogProgressbar.Maximum = 100;
                                                        LogProgressbar.Value = 0;
                                                        ProgressBarText.Text = "";
                                                    }
                                                    else
                                                    {
                                                        LogProgressbar.Maximum = e.Max;
                                                        LogProgressbar.Value = e.Value;
                                                        if ( !string.IsNullOrEmpty( e.Text ) )
                                                        {
                                                            ProgressBarText.Text = e.Text + " " + e.Value + " / " + e.Max;
                                                        }
                                                        else
                                                        {
                                                            ProgressBarText.Text = "";
                                                        }
                                                    }
                                                } ) );
        }

        private void LevelUp( object sender, EventArgs eventArgs )
        {
            var p = ( Player ) sender;
            Bot.Print( "MainWindow received a level up event. ( new level: " + p.Level + " )", System.Drawing.Color.MediumPurple );
        }

        private void OnClosed( object sender, EventArgs eventArgs )
        {
            if ( !Bot.Stopped )
            {
                Bot.Stop();
            }
            Bot.StopCheckingForNewInstances();
        }

        private void CurrentDomainOnUnhandledException( object sender, UnhandledExceptionEventArgs args )
        {
            var ex = args.ExceptionObject as Exception;
            if ( ex != null )
            {
                try
                {
                    if ( File.Exists( Bot.DataPath + "Log.txt" ) )
                    {
                        File.Delete( Bot.DataPath + "Log.txt" );
                    }
                    using ( var sw = File.CreateText( Bot.DataPath + "Log.txt" ) )
                    {
                        var ie = ex.InnerException;
                        if ( ie != null )
                        {
                            do
                            {
                                sw.WriteLine( "Message: " + ie.Message );
                                sw.WriteLine( "Stacktrace: " + ie.StackTrace );
                                ie = ie.InnerException;
                            } while ( ie != null );
                        }
                        else
                        {
                            sw.WriteLine( "Message: " + ex.Message );
                            sw.WriteLine( "Stacktrace: " + ex.StackTrace );
                        }
                        sw.Flush();
                    }
                }
                catch
                {
                }
                if ( !args.IsTerminating )
                {
                    Bot.Error( ex.Message );
                }
                else
                {
                    MessageBox.Show( "We found an error and we need to close.\n" + ex.Message, "Error" );
                }
            }
            else
            {
                if ( !args.IsTerminating )
                {
                    Bot.Error( "We found an unkown error." );
                }
                else
                {
                    MessageBox.Show( "We found an unkown error and we need to close.", "Error" );
                }
            }
        }

        private void LoggerOnLogArrived( LogEventArgs args )
        {
            Dispatcher.BeginInvoke( new Action( () =>
                                                {
                                                    try
                                                    {
                                                        var tr = new TextRange( this.TbLog.Document.ContentEnd, this.TbLog.Document.ContentEnd )
                                                        {
                                                            Text = args.Text
                                                        };
                                                        if ( args.Color == System.Drawing.Color.White )
                                                        {
                                                            tr.ApplyPropertyValue( TextElement.ForegroundProperty, Brushes.White );
                                                        }
                                                        else
                                                        {
                                                            var brush = new SolidColorBrush( Color.FromRgb( args.Color.R, args.Color.G, args.Color.B ) );
                                                            tr.ApplyPropertyValue( TextElement.ForegroundProperty, brush );
                                                        }
                                                    }
                                                    catch ( StackOverflowException )
                                                    {
                                                        this.TbLog.Document.Blocks.Clear();
                                                    }
                                                    finally
                                                    {
                                                        this.TbLog.ScrollToEnd();
                                                    }
                                                } ) );
        }

        void DispatcherTimerTick( object sender, EventArgs e )
        {
            Dispatcher.BeginInvoke( new UpdateUiDelegate( UpdateUI ) );
        }

        private void LoadProfiles()
        {
            this.profiles.Clear();
            this.CbProfile.Items.Clear();
            if ( !Bot.Initialized )
            {
                Bot.CheckDirectories();
            }
            var files = new DirectoryInfo( Bot.DataPath + @"Profiles" ).GetFiles( "*.xml" );

            foreach ( var f in files )
            {
                this.profiles.Add( f.FullName );
                this.CbProfile.Items.Add( f.Name.Remove( f.Name.Length - 4 ) );
            }
            if ( Bot.Initialized && Bot.Player != null )
            {
                this.CbProfile.SelectedValue = Bot.Player.Name;
                this.CbProfile.SelectedItem = Bot.Player.Name;
                BtnLoadClick( btnLoad, new RoutedEventArgs() );
            }
        }

        private void LoadWaypoints()
        {
            this.waypoints.Clear();
            this.CbWaypointsFile.Items.Clear();
            this.CbWaypointsReturnFile.Items.Clear();

            var files = new DirectoryInfo( Bot.DataPath + @"Waypoints" ).GetFiles( "*.xml", SearchOption.AllDirectories );

            this.waypoints.Add( "" );
            this.CbWaypointsFile.Items.Add( "" );
            this.CbWaypointsReturnFile.Items.Add( "" );

            foreach ( var f in files )
            {
                this.waypoints.Add( f.FullName );
                if ( f.Directory != null && f.Directory.Name != "Waypoints" )
                {
                    this.CbWaypointsFile.Items.Add( f.Directory.Name + "/" + f.Name );
                    this.CbWaypointsReturnFile.Items.Add( f.Directory.Name + "/" + f.Name );
                }
                else
                {
                    this.CbWaypointsFile.Items.Add( f.Name );
                    this.CbWaypointsReturnFile.Items.Add( f.Name );
                }
            }

        }

        private void BtnAgregarMobClick( object sender, RoutedEventArgs e )
        {
            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;

            var node = provider.Document.CreateNode( System.Xml.XmlNodeType.Element, "mob", "" );
            var attr = provider.Document.CreateAttribute( "name" );
            attr.Value = this.TbFriendName.Text;
            node.Attributes.Append( attr );
            provider.Document[ "profile" ][ "mobs" ].AppendChild( node );
        }

        private void BtnAgregarFriendClick( object sender, RoutedEventArgs e )
        {
            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;

            var node = provider.Document.CreateNode( System.Xml.XmlNodeType.Element, "friend", "" );
            var attr = provider.Document.CreateAttribute( "name" );
            attr.Value = this.TbFriendName.Text;
            node.Attributes.Append( attr );
            provider.Document[ "profile" ][ "friends" ].AppendChild( node );
        }

        private void FriendsOrMobsGridsKeyUp( object sender, System.Windows.Input.KeyEventArgs e )
        {
            if ( e.Key == System.Windows.Input.Key.Delete )
            {
                var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;
                var nodeName = sender == this.MobsGrid ? "mobs" : "friends";

                var node = ( ( DataGrid ) sender ).SelectedItem as XmlNode;
                provider.Document[ "profile" ][ nodeName ].RemoveChild( node );
            }
        }

        private void BtnLoadClick( object sender, RoutedEventArgs e )
        {
            if ( !Bot.Initialized )
            {
                //Make sure Database is there
                Bot.CheckDirectories();
            }
            if ( this.CbProfile.SelectedIndex < 0 )
            {
                Title = "RomBotUI";
                return;
            }

            this.TabControl.IsEnabled = true;

            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;
            if ( provider != null && File.Exists( this.profiles[ this.CbProfile.SelectedIndex ] ) )
            {
                provider.Source = new Uri( this.profiles[ this.CbProfile.SelectedIndex ] );
                provider.Refresh();
            }
            else
            {
                return;
            }

            //this.skillsProvider = this.Resources[ "skillsDataProvider" ] as XmlDataProvider;
            if ( this.skillsProvider != null )
            {
                this.skillsProvider.Source = new Uri( Bot.DataPath + @"Database\skills.xml" );
                this.skillsProvider.Refresh();
            }

            var skillsLocal = new List<Skill>();
            var doc = XDocument.Load( Bot.DataPath + @"Database\skills.xml" );

            var allSkills = ( from o in doc.Descendants()
                              where o.HasAttributes
                              select new Skill
                              {
                                  Name = o.Attribute( "name" ).Value,
                                  ID = o.Name.ToString()
                              } ).ToList();

            var resu = ( from o in doc.Descendants()
                         where o.Attribute( XName.Get( "es", "" ) ) != null
                         select new Skill
                         {
                             Name = o.Attribute( XName.Get( "es", "" ) ).Value,
                             ID = o.Name.ToString()
                         } ).ToList();


            var classNames = new Dictionary<string, string>();

            var skillClassName = allSkills.Select( s => s.ID.Split( '_' )[ 0 ].ToLower() ).Distinct();
            foreach ( var skill in skillClassName )
            {
                var name = skill;//.Split( '_' )[ 0 ].ToLower();
                name = name[ 0 ].ToString( CultureInfo.InvariantCulture ).ToUpper() + name.Substring( 1 );
                classNames.Add( skill, name );
            }

            foreach ( var skill in allSkills )
            {
                var localSkill = resu.FirstOrDefault( r => r.ID == skill.ID );

                if ( localSkill != null )
                {
                    skill.Name = classNames[ skill.ID.Split( '_' )[ 0 ].ToLower() ] + ": " + localSkill.Name;
                }
                else
                {
                    skill.Name = classNames[ skill.ID.Split( '_' )[ 0 ].ToLower() ] + ": " + skill.Name;
                }

                skillsLocal.Add( skill );
            }

            this.CbSkills.ItemsSource = skillsLocal.OrderBy( s => s.Name );

            if ( Bot.Initialized && Bot.Player != null )
            {
                Bot.ForcedProfile = null;
                Bot.Player.Profile.Load( this.profiles[ this.CbProfile.SelectedIndex ], true );
            }
            else
            {
                Bot.ForcedProfile = this.profiles[ this.CbProfile.SelectedIndex ];
            }
        }

        private void BtnSaveClick( object sender, RoutedEventArgs e )
        {
            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;
            provider.Document.Save( provider.Source.LocalPath );
            if ( Bot.Initialized && Bot.Player != null )
            {
                //Force a reload
                Bot.Player.Profile.Load( this.profiles[ this.CbProfile.SelectedIndex ], true );
            }
        }

        private void CbCombatTypeSelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            //var x = cbCombatType.SelectedIndex;
        }

        void StartBot()
        {
            this.context = SynchronizationContext.Current;
            Bot.Instance.Contexto = this.context;
            if ( !Bot.Initialized )
            {
                if ( !Bot.Instancia.Initialize() )
                {
                    throw new Exception( "No se pudo inicializar el bot" );
                }
                Bot.Player.PlayerUpdated += ( sender, args ) => this.Dispatcher.BeginInvoke( new Action( this.InitializeUI ) );

                Bot.Player.LevelUp += LevelUp;
                InitializeUI();
                this.GrillaSurrounding.ItemsSource = surrounding;

                dispatcherTimer.Tick += this.DispatcherTimerTick;
                dispatcherTimer.Interval = TimeSpan.FromMilliseconds( 500 );

                dispatcherTimer.Start();
            }
            if ( Bot.Paused )
            {
                Bot.Resume();
            }
            else
            {
                Bot.Start();
            }
        }

        private void BtnStartClick( object sender, RoutedEventArgs e )
        {
            btnStop.IsEnabled = true;
            btnPause.IsEnabled = true;
            btnStart.IsEnabled = false;
            BtnAssistMode.IsEnabled = false;
            Bot.AssistMode = false;

            StartBot();
        }

        private void BtnStopClick( object sender, RoutedEventArgs e )
        {
            Bot.Instancia.Stop();
            btnStop.IsEnabled = false;
            btnPause.IsEnabled = false;
            btnStart.IsEnabled = true;
            BtnAssistMode.IsEnabled = true;
            this.TbStatus.Text = "Stopped";
        }

        private void CbSkillsClassSelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;
            provider.Source = new Uri( this.profiles[ this.CbProfile.SelectedIndex ] );

            var binding = new Binding { Source = provider, Mode = BindingMode.TwoWay };

            var selected = ( ( ComboBoxItem ) this.CbSkillsClass.SelectedValue ).Content.ToString().ToLower();
            if ( selected == "common" )
            {
                selected = null;
            }

            binding.XPath = "/profile/skills" + ( !string.IsNullOrEmpty( selected ) ? "_" + selected : "" );
            if ( !binding.XPath.EndsWith( "/" ) )
                binding.XPath += "/";
            binding.XPath += "skill";

            this.SkillsGrid.SetBinding( ItemsControl.ItemsSourceProperty, binding );

            if ( this.CbSkillsClass.SelectedIndex >= 0 )
            {
                this.BtnAddSkill.IsEnabled = true;
            }
        }

        private void BtnAddSkillClick( object sender, RoutedEventArgs e )
        {
            //XElement newSkill = new XElement( "skill", new XAttribute( "name", tbName.Text ), new XAttribute( "modifier", tbModifier.Text ), new XAttribute( "priority", tbPriority.Text ) );
            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;

            var selected = ( ( ComboBoxItem ) this.CbSkillsClass.SelectedValue ).Content.ToString().ToLower();
            if ( selected == "common" )
            {
                selected = null;
            }

            var nodeName = "skills" + ( !string.IsNullOrEmpty( selected ) ? "_" + selected : "" );
            var node = provider.Document.CreateNode( System.Xml.XmlNodeType.Element, "skill", "" );
            var attr = provider.Document.CreateAttribute( "name" );
            //attr.Value = tbName.Text;
            attr.Value = this.CbSkills.Text.Replace( "Skill: ", "" );
            node.Attributes.Append( attr );

            attr = provider.Document.CreateAttribute( "modifier" );
            if ( this.CbModifier.SelectedValue != null && !string.IsNullOrEmpty( ( ( ComboBoxItem ) this.CbModifier.SelectedValue ).Content.ToString() ) )
            {
                attr.Value = ( ( ComboBoxItem ) this.CbModifier.SelectedValue ).Content.ToString();
            }
            node.Attributes.Append( attr );

            attr = provider.Document.CreateAttribute( "hotkey" );
            if ( !string.IsNullOrEmpty( this.TbHotKey.Text ) )
            {
                attr.Value = this.TbHotKey.Text;
            }
            else
            {
                attr.Value = "MACRO";
            }
            node.Attributes.Append( attr );

            if ( string.IsNullOrEmpty( this.TbPriority.Text ) )
            {
                System.Windows.MessageBox.Show( "You have to set up a priority for the skill." );
                return;
            }
            attr = provider.Document.CreateAttribute( "priority" );
            attr.Value = this.TbPriority.Text;
            node.Attributes.Append( attr );

            if ( !string.IsNullOrEmpty( this.TbHpPer.Text ) )
            {
                attr = provider.Document.CreateAttribute( "hpper" );
                attr.Value = this.TbHpPer.Text;
                node.Attributes.Append( attr );
            }

            if ( !string.IsNullOrEmpty( this.TbMpPer.Text ) )
            {
                attr = provider.Document.CreateAttribute( "manaper" );
                attr.Value = this.TbMpPer.Text;
                node.Attributes.Append( attr );
            }

            if ( this.ChbInBattle.IsChecked != null )
            {
                attr = provider.Document.CreateAttribute( "inbattle" );
                attr.Value = ( this.ChbInBattle.IsChecked ?? false ).ToString().ToLower();
                node.Attributes.Append( attr );
            }

            if ( !string.IsNullOrEmpty( this.TbTargetHp.Text ) )
            {
                attr = provider.Document.CreateAttribute( "targethp" );
                attr.Value = this.TbTargetHp.Text;
                node.Attributes.Append( attr );
            }

            if ( !string.IsNullOrEmpty( this.TbTargetHpPer.Text ) )
            {
                attr = provider.Document.CreateAttribute( "targethpper" );
                attr.Value = this.TbTargetHpPer.Text;
                node.Attributes.Append( attr );
            }

            if ( !string.IsNullOrEmpty( this.TbRebuffCut.Text ) )
            {
                attr = provider.Document.CreateAttribute( "rebuffcut" );
                attr.Value = this.TbRebuffCut.Text;
                node.Attributes.Append( attr );
            }

            if ( !string.IsNullOrEmpty( this.TbCoolDown.Text ) )
            {
                attr = provider.Document.CreateAttribute( "cooldown" );
                attr.Value = this.TbCoolDown.Text;
                node.Attributes.Append( attr );
            }

            if ( this.ChbPullOnly.IsChecked != null )
            {
                attr = provider.Document.CreateAttribute( "pullonly" );
                attr.Value = ( this.ChbPullOnly.IsChecked ?? false ).ToString().ToLower();
                node.Attributes.Append( attr );
            }

            if ( !string.IsNullOrEmpty( this.TbMaxUse.Text ) )
            {
                attr = provider.Document.CreateAttribute( "maxuse" );
                attr.Value = this.TbMaxUse.Text;
                node.Attributes.Append( attr );
            }

            if ( this.ChbAutoUse.IsChecked != null )
            {
                attr = provider.Document.CreateAttribute( "autouse" );
                attr.Value = ( this.ChbAutoUse.IsChecked ?? true ).ToString().ToLower();
                node.Attributes.Append( attr );
            }

            if ( provider.Document[ "profile" ][ nodeName ] == null )
            {
                provider.Document[ "profile" ].AppendChild( provider.Document.CreateNode( System.Xml.XmlNodeType.Element, nodeName, "" ) );
            }

            var oldNode = provider.Document.SelectSingleNode( "descendant::" + nodeName + "/skill[@name='" + node.Attributes[ "name" ].Value + "']" );


            if ( oldNode == null )
            {
                provider.Document[ "profile" ][ nodeName ].AppendChild( node );
            }
            else
            {
                provider.Document[ "profile" ][ nodeName ].ReplaceChild( node, oldNode );
            }
        }

        private void skillsGrid_KeyUp( object sender, System.Windows.Input.KeyEventArgs e )
        {
            if ( e.Key == System.Windows.Input.Key.Delete )
            {
                var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;
                var selected = ( ( ComboBoxItem ) this.CbSkillsClass.SelectedValue ).Content.ToString().ToLower();
                var nodeName = "skills" + ( !string.IsNullOrEmpty( selected ) && selected != "common" ? "_" + selected : "" );

                var node = this.SkillsGrid.SelectedItem as XmlNode;
                provider.Document[ "profile" ][ nodeName ].RemoveChild( node );
                Skills.ProcessPlayerSkills();
            }
        }

        public static Dictionary<PawnClass, SolidColorBrush> TipoClaseColorEnergia = new Dictionary<PawnClass, SolidColorBrush>{
                {PawnClass.None,Brushes.Transparent},
                {PawnClass.NotDefined,Brushes.Transparent},
                { PawnClass.Warrior, Brushes.OrangeRed },
                {PawnClass.Scout, Brushes.Green},
                {PawnClass.Rogue, Brushes.BlueViolet},
                {PawnClass.Mage,Brushes.Blue},
                {PawnClass.Priest,Brushes.Blue},
                {PawnClass.Knight,Brushes.Blue},
                {PawnClass.Warden, Brushes.Blue},
                {PawnClass.Druid, Brushes.Blue},
                {PawnClass.Warlock, Brushes.Green},
                {PawnClass.Champion, Brushes.OrangeRed},
        };
        private WaypointList _wpl;

        private void UpdateUI()
        {
            if ( !Bot.Initialized || !RoMBot.Helpers.IsInGame )
            {
                return;
            }

            this.Title = string.Format( RoMBot.Properties.Resources.WindowTitle, player.Name, Bot.TimeToLevel );

            if ( this.GrillaSurrounding.IsVisible )
            {
                if ( SurroundingOnlyMobs.IsChecked == true || SurroundingOnlyTargetRange.IsChecked == true )
                {
                    surrounding.Update( false, null, o => ( this.SurroundingOnlyMobs.IsChecked != true || ( o.Attackable && !o.IsPet ) ) && ( this.SurroundingOnlyTargetRange.IsChecked != true || o.DistanceTo( this.player ) <= this.player.Profile.GetOptionValue<int>( "MAX_TARGET_DISTANCE" ) ), true );
                }
                else
                {
                    surrounding.Update( false, nonParallel: true );
                }
                Thread.Sleep( 0 );
            }
            if ( InventoryGrid.IsVisible )
            {
                player.Inventory.Update();
                foreach ( var item in player.Inventory.Items )
                {
                    //Its a new one, lets add it
                    if ( item.ItemCount > 0 && !inventoryStatistics.ContainsKey( item.Id ) )
                    {
                        inventoryStatistics.Add( item.Id, 0 );
                        inventoryItems.Add( new InventoryItemStatistics( item, 0 ) );
                    }
                    else
                    {
                        var inInv = inventoryItems.FirstOrDefault( i => i.Id == item.Id );
                        if ( inInv != null )
                        {
                            inInv.ItemCount = this.player.Inventory.GetItemCount( item.Id, false );
                            inventoryItems[ inventoryItems.IndexOf( inInv ) ] = inInv;
                        }
                    }
                }
            }

            if ( GridMobsKilled.IsVisible && Bot.Player.StatisticsKilledMobs.Count > 0 )
            {
                var mobs = from mob in Bot.Player.StatisticsKilledMobs
                           group mob by mob.Name into g
                           select
                                   new
                                   {
                                       Name = g.Key,
                                       Level = g.Max( m => m.Level ),
                                       Total = g.Count(),
                                       Exp = g.Sum( m => m.Exp ),
                                       Tp = g.Sum( m => m.Tp ),
                                       AverageTime = Math.Round( g.Average( m => m.Time.TotalSeconds / 60 ), 2 )
                                   };
                var total = new
                            {
                                Name = "Total",
                                Level = ( int ) Bot.Player.StatisticsKilledMobs.Average( m => m.Level ),
                                Total = Bot.Player.StatisticsKilledMobs.Count,
                                Exp = Bot.Player.StatisticsKilledMobs.Sum( m => m.Exp ),
                                Tp = Bot.Player.StatisticsKilledMobs.Sum( m => m.Tp ),
                                AverageTime = Math.Round( Bot.Player.StatisticsKilledMobs.Average( m => m.Time.TotalSeconds / 60 ), 2 )
                            };

                GridMobsKilled.ItemsSource = mobs.Union( new[] { total } ).ToList();
            }

            if ( GrillaWaypoints.IsVisible )
            {
                GrillaWaypoints.SelectedIndex = Bot.CurrentWaypointList.CurrentIndex;
            }

            if ( !GrillaPlayerStatus.IsVisible )
            {
                return;
            }
            this.player.Update();

            if ( this.HpBar.Maximum != this.player.MaxHP )
            {
                this.HpBar.Maximum = this.player.MaxHP;
            }

            ExpBar.Value = player.Exp;
            TbExp.Text = player.Exp + " / " + player.MaxExp;
            TbMinutesToLevel.Text = "Time to level: " + Bot.TimeToLevel.ToString( "hh\\:mm\\:ss" );
            LblPlayerTp.Content = "TP: " + player.Tp;

            this.HpBar.Value = this.player.HP;
            this.TbHp.Text = this.player.HP + " / " + this.player.MaxHP;

            this.MpBar.Value = this.player.MP;
            this.TbMp.Text = this.player.MP + " / " + this.player.MaxMP;
            this.Mp2Bar.Value = this.player.MP2;
            this.TbMp2.Text = this.player.MP2 + " / " + this.player.MaxMP2;

            this.LblPlayerX.Content = "X: " + this.player.X;
            this.LblPlayerY.Content = "Y: " + this.player.Y;
            this.LblPlayerZ.Content = "Z: " + this.player.Z;

            this.ChbBattling.IsChecked = ( bool? ) ( this.player.Battling );
            this.ChbHarvesting.IsChecked = ( bool? ) ( this.player.Harvesting );

            if ( this.player.HasTarget )
            {
                var targetText = "Target: " + this.player.Target.Name;
                if ( this.player.Target.Type > 0 && ( this.player.Target.Class1 > 0 || this.player.Target.Class2 > 0 ) )
                {
                    targetText += " - [";
                    if ( this.player.Target.Class1 > 0 )
                    {
                        targetText += this.player.Target.Class1;
                    }
                    if ( this.player.Target.Class2 > 0 )
                    {
                        targetText += " / " + this.player.Target.Class2;
                    }
                    targetText += "]";
                }
                this.LblTargetName.Content = targetText;
                this.TargetHpBar.Maximum = this.player.Target.MaxHP;
                this.TargetHpBar.Value = this.player.Target.HP;
                this.TbTargetHpBar.Text = this.player.Target.HP + " / " + this.player.Target.MaxHP;
            }
            else
            {
                this.LblTargetName.Content = "Target: [None]";
                this.TargetHpBar.Maximum = 1;
                this.TargetHpBar.Value = 0;
                this.TbTargetHpBar.Text = "0 / 0";
            }

            if ( this.player.Target != null )
            {
                this.TbLastDamage.Text = "Last damage: " + this.player.Target.LastDamage;
            }
            //else
            //{
            //    tbLastDamage.Text = "Last damage: 0";
            //}

            if ( this.player.Buffs.Count > 0 )
            {
                this.TbBuffs.Text = "Buffs: ";
                foreach ( var buff in this.player.Buffs )
                {
                    this.TbBuffs.Text += buff.Name + ( buff != this.player.Buffs.Last() ? ", " : "" );
                }
            }
            else
            {
                this.TbBuffs.Text = "Buffs: [None]";
            }
        }

        private void CbWaypointsFileSelectionChanged1( object sender, SelectionChangedEventArgs e )
        {
            if ( this.CbWaypointsFile.SelectedIndex <= 0 )
                return;
            e.Handled = true;
            if ( AutoCreatingWaypoints )
            {
                this.CbWaypointsFile.SelectedValue = "";
                return;
            }
            SelectedPath = this.waypoints[ this.CbWaypointsFile.SelectedIndex ];
            this._wpl = new WaypointList( this.waypoints[ this.CbWaypointsFile.SelectedIndex ] );
            if ( Bot.Initialized )
            {
                this._wpl.Current = this._wpl.GetNearestWaypoint( Bot.Player.X, Bot.Player.Z );
            }
            Bot.Instancia.WaypointList = this._wpl;
            GrillaWaypoints.ItemsSource = Bot.Instance.WaypointList.Waypoints;
        }

        public static string SelectedPath { get; set; }

        private void ButtonMapClick( object sender, RoutedEventArgs e )
        {
            var visual = new VisualMap();
            visual.Show();
        }

        private void BtnExecuteCodeClick( object sender, RoutedEventArgs e )
        {
            try
            {
                if ( !Bot.Stopped )
                {
                    MessageBox.Show( "The bot must be stopped to execute console code.", "Error" );
                    return;
                }
                this.TabControl.SelectedIndex = 2; //We change it here so the user can see the log.

                if ( !Bot.Initialized )
                {
                    Bot.Initialize();
                }
                var textRange = new TextRange( tbCodigo.Document.ContentStart, tbCodigo.Document.ContentEnd );
                var code = textRange.Text;

                btnEjecutarCodigo.IsEnabled = false;
                Bot.RunUserCode( code );
            }
            catch ( Exception ex )
            {
                btnEjecutarCodigo.IsEnabled = true;
                Bot.Error( ex.Message );
            }
            finally
            {
                btnEjecutarCodigo.IsEnabled = true;
            }
        }

        private void btnPause_Click( object sender, RoutedEventArgs e )
        {
            Bot.Pause();
        }

        private bool AutoCreatingWaypoints { get; set; }

        private void BtnAutoCreateWaypointListClick( object sender, RoutedEventArgs e )
        {
            AutoCreatingWaypoints = !AutoCreatingWaypoints;
            if ( AutoCreatingWaypoints )
            {
                var list = new WaypointList();
                this.GrillaWaypoints.ItemsSource = list.Waypoints;
                Bot.Instancia.CreatingWaypointList = true;
                var bg = new BackgroundWorker();
                bg.DoWork += CreateWayPointList;
                bg.ProgressChanged += ( o, args ) => Thread.Sleep( 0 );
                bg.RunWorkerCompleted += ( o, args ) =>
                                         {
                                             this.Focus();
                                             this.BringIntoView();
                                             var dialog = new System.Windows.Forms.SaveFileDialog();
                                             var result = dialog.ShowDialog();

                                             if ( result == System.Windows.Forms.DialogResult.OK )
                                             {
                                                 list.Save( dialog.FileName );
                                                 LoadWaypoints();
                                             }
                                             Bot.Instancia.CreatingWaypointList = false;
                                         };
                bg.RunWorkerAsync( list );
            }
            else
            {
                var t = 0;
            }
        }

        private void CreateWayPointList( object sender, DoWorkEventArgs e )
        {
            var list = ( WaypointList ) e.Argument;
            Keyboard.ReleaseKey( Key.VK_NUMPAD1 );
            Keyboard.ReleaseKey( Key.VK_NUMPAD2 );
            Keyboard.ReleaseKey( Key.VK_NUMPAD3 );
            Keyboard.ReleaseKey( Key.VK_NUMPAD4 );
            while ( AutoCreatingWaypoints )
            {
                Thread.CurrentThread.Join( 50 );
                if ( Keyboard.IsKeyPressedInApp( Key.VK_NUMPAD1 ) )
                {
                    Keyboard.ReleaseKey( Key.VK_NUMPAD1 );
                    Thread.CurrentThread.Join( 100 );
                    Dispatcher.BeginInvoke( new Action( () =>
                                                        {
                                                            list.Add( this.player.X, this.player.Z, this.player.Y );
                                                            RoMBot.Helpers.SendMessage( "Added Waypoint #" + list.Count );
                                                        } ) );
                    Thread.CurrentThread.Join( 50 );
                }

                Thread.CurrentThread.Join( 50 );
                if ( Keyboard.IsKeyPressedInApp( Key.VK_NUMPAD2 ) )
                {
                    Keyboard.ReleaseKey( Key.VK_NUMPAD2 );
                    Thread.CurrentThread.Join( 100 );
                    Dispatcher.BeginInvoke( new Action( () =>
                    {
                        list.Add( player.X, player.Z, player.Y, null, WaypointType.Harvest );
                        RoMBot.Helpers.SendMessage( "Added Waypoint (harvest) #" + list.Count );
                    } ) );
                    Thread.CurrentThread.Join( 50 );
                }
                Thread.CurrentThread.Join( 50 );
                if ( Keyboard.IsKeyPressedInApp( Key.VK_NUMPAD4 ) )
                {
                    Keyboard.ReleaseKey( Key.VK_NUMPAD4 );
                    Thread.CurrentThread.Join( 100 );
                    player.UpdateTarget();
                    if ( player.Target != null )
                    {
                        Thread.CurrentThread.Join( 100 );
                        player.Target.UpdateName();
                        var name = player.Target.Name;
                        Thread.CurrentThread.Join( 100 );
                        Dispatcher.BeginInvoke( new Action( () =>
                        {
                            list.Add( player.X, player.Z, player.Y, string.Format( "Player.TargetNpc(\"{0}\");", name ) );
                            RoMBot.Helpers.SendMessage( "Added Waypoint (TargetNpc) #" + list.Count );
                        } ) );
                    }
                    Thread.CurrentThread.Join( 50 );
                }

                Thread.CurrentThread.Join( 50 );
                if ( Keyboard.IsKeyPressedInApp( Key.VK_NUMPAD3 ) )
                {
                    Keyboard.ReleaseKey( Key.VK_NUMPAD3 );
                    AutoCreatingWaypoints = false;
                    break;
                }

                Thread.CurrentThread.Join( 100 );
            }
        }

        private void InventoryGridSelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            //var item = ( InventoryItem ) e.AddedItems[ 0 ];
            //var original = inventoryStatistics.ContainsKey( item.Id ) ? inventoryStatistics[ item.Id ] : 0;
            //var now = player.Inventory.GetItemCount( item.Id );
        }

        private class InventoryItemStatistics : InventoryItem
        {
            private int originalItemCount;

            public InventoryItemStatistics( InventoryItem original )
            {
                this.SlotNumber = original.SlotNumber;
                this.Id = original.Id;
                this.Name = original.Name;
                this.ItemCount = originalItemCount = original.ItemCount;
            }

            public InventoryItemStatistics( InventoryItem original, int count )
                : this( original )
            {
                this.originalItemCount = count;
            }

            public int Difference
            {
                get { return this.ItemCount - originalItemCount; }
            }
        }

        private void BtnAssistModeClick( object sender, RoutedEventArgs e )
        {
            btnStop.IsEnabled = true;
            btnPause.IsEnabled = true;
            btnStart.IsEnabled = false;
            BtnAssistMode.IsEnabled = false;
            Bot.AssistMode = true;

            StartBot();
        }

        private void SkillsGridOnRowEditEnding( object sender, DataGridRowEditEndingEventArgs e )
        {
            if ( e.EditAction != DataGridEditAction.Commit )
            {
                return;
            }
            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;
            var node = this.SkillsGrid.SelectedItem as XmlNode;
            if ( provider == null || node == null || node.Attributes == null || node.Attributes[ "name" ] == null )
            {
                return;
            }
            var profileElement = provider.Document[ "profile" ];
            if ( profileElement == null )
            {
                return;
            }
            var selected = ( ( ComboBoxItem ) this.CbSkillsClass.SelectedValue ).Content.ToString().ToLower();
            var nodeName = "skills" + ( !string.IsNullOrEmpty( selected ) && selected != "common" ? "_" + selected : "" );
            var skillsElement = profileElement[ nodeName ];
            if ( skillsElement == null )
            {
                return;
            }
            var nodes = skillsElement.SelectNodes( "skill[@name='" + node.Attributes[ "name" ].Value + "']" );
            if ( nodes != null && nodes.Count > 0 )
            {
                var existingNode = nodes[ 0 ];//.SelectSingleNode( "@name=" + node.Attributes[ "name" ].Value );
                if ( existingNode == null )
                {
                    return;
                }
                skillsElement.ReplaceChild( node, existingNode );
                var playerSkill = Bot.Player.Profile.Skills.Values.SingleOrDefault( s => s.ID == node.Attributes[ "name" ].Value );
                if ( playerSkill != null )
                {
                    playerSkill.UpdateFromNode( node );
                    Skills.UpdateProfileSkill( playerSkill );
                }
            }
        }

        private void SkillsGridOnCellEditEnding( object sender, DataGridCellEditEndingEventArgs e )
        {
            var provider = this.TabControl.Resources[ "dataProvider" ] as XmlDataProvider;
            var node = this.SkillsGrid.SelectedItem as XmlNode;
            if ( provider == null || node == null )
            {
                return;
            }
            var path = e.Column.SortMemberPath.Replace( "@", "" );
            var val = e.EditingElement is TextBox ? e.EditingElement.GetValue( TextBox.TextProperty ) : e.EditingElement.GetValue( ToggleButton.IsCheckedProperty );
            if ( node.Attributes[ path ] != null )
            {
                // This may look redundant but we need it here because, if we don't explicitly set it here, it won't have the value when we check on row ending...
                node.Attributes[ path ].Value = val != null ? val.ToString().ToLowerInvariant() : "";
                return;
            }
            var att = provider.Document.CreateAttribute( path );
            att.Value = val != null ? val.ToString().ToLowerInvariant() : "";
            node.Attributes.Append( att );
        }

        private void ButtonReverseClick( object sender, RoutedEventArgs e )
        {
            Bot.CurrentWaypointList.Reverse();
            GrillaWaypoints.ItemsSource = Bot.Instancia.CurrentWaypointList.Waypoints;
        }

        private void CbReturnWaypointsFileSelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            if ( this.CbWaypointsReturnFile.SelectedIndex <= 0 )
                return;
            e.Handled = true;
            if ( AutoCreatingWaypoints )
            {
                this.CbWaypointsReturnFile.SelectedValue = "";
                return;
            }
            var wpl = new WaypointList( this.waypoints[ this.CbWaypointsReturnFile.SelectedIndex ] );
            if ( Bot.Initialized )
            {
                wpl.Current = wpl.GetNearestWaypoint( Bot.Player.X, Bot.Player.Z );
            }
            Bot.Instance.ReturnWaypointList = wpl;
        }

        private void CbGameInstancesOnSelectionChanged( object sender, SelectionChangedEventArgs e )
        {
            var instanceName = ( string ) e.AddedItems[ 0 ];
            Bot.ProcId = Bot.GameInstances[ instanceName ];
            CbProfile.SelectedValue = instanceName;
            if ( CbProfile.SelectedItem != null )
            {
                this.BtnLoadClick( btnLoad, null );
            }
            else
            {
                Bot.ForcedProfile = Bot.DataPath + "Profiles\\Default.xml";
            }
            Bot.ProcId = Bot.GameInstances[ instanceName ];

            if ( RoMBot.Helpers.IsInGame && !Bot.Initialized )
            {
                Dispatcher.BeginInvoke( new Func<bool>( () => Bot.Initialize() ) );
            }
        }

        private void CbGameInstances_DropDownOpened( object sender, EventArgs e )
        {
            Bot.CheckProcessStart();
        }
    }
}
